﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using EAEcoreAddin.SQLWrapperClasses;
using System.Collections;

namespace EAEcoreAddin.Util
{
    class EAUtil
    {
        public static List<String> getXMLNodeContentFromSQLQueryString(String queryString, String rowName)
        {
            List<String> returnVal = new List<string>();

            String editedString = "<?xml version=\"1.0\"?>\r\n<DUMMY>" + queryString + "</DUMMY>";
            Char[] array = queryString.ToCharArray();
            if (queryString != "")
            {
                if (array[1] != '?')
                    queryString = editedString;

                // load contents of file
                TextReader textReader = new StringReader(queryString);

                // process file contents
                XmlDocument domDoc = new XmlDocument();
                domDoc.Load(textReader);

                XmlNodeList nodeList = domDoc.GetElementsByTagName(rowName);
                foreach (XmlNode node in nodeList)
                {
                    returnVal.Add(node.InnerXml);
                }

            }
            if (returnVal.Count == 0)
                returnVal.Add("");
            return returnVal;
        }

        public static List<String> getDiagramIDsOfObject(EA.Element element, SQLRepository repository)
        {
            String result = repository.SQLQuery("select Diagram_ID from t_diagramobjects where Object_ID = " + element.ElementID);

            return getXMLNodeContentFromSQLQueryString(result, "Diagram_ID");
        }

        public static ArrayList getBaseClasses(EA.Element classToGet)
        {
            return getBaseClasses(classToGet, new ArrayList());
        }

        private static ArrayList getBaseClasses(EA.Element classToGet, ArrayList returnList)
        {
            EA.Element actClass = classToGet;
            returnList.Add(actClass);
            foreach (EA.Element actBaseClass in actClass.BaseClasses)
            {
                returnList = getBaseClasses(actBaseClass, returnList);
            }
            return returnList;
        }

        /// <summary>
        /// Searches for a TaggedValue with the given name on the given Element
        /// </summary>
        /// <param name="element"></param>
        /// <param name="name"></param>
        /// <returns>the found TaggedValue or null</returns>
        public static EA.TaggedValue findTaggedValue(EA.Element element, String name)
        {
            EA.TaggedValue aTag = null;
            if (element != null)
            {

                element.TaggedValues.Refresh();
                foreach (EA.TaggedValue aTaggi in element.TaggedValues)
                {
                    if (aTaggi.Name == name)
                        aTag = aTaggi;
                }
            }
            return aTag;
        }

        /// <summary>
        /// Searches for a MethodTag with the given name on the given Method
        /// </summary>
        /// <param name="element"></param>
        /// <param name="name"></param>
        /// <returns>the found MethodTag or null</returns>
        public static EA.MethodTag findTaggedValue(EA.Method method, String name)
        {
            method.TaggedValues.Refresh();
            EA.MethodTag aTag = null;

            foreach (EA.MethodTag aTaggi in method.TaggedValues)
            {
                if (aTaggi.Name == name) aTag = aTaggi;
            }

            return aTag;
        }
        /// <summary>
        /// Searches for a ConnectorTag with the given name on the given Connector
        /// </summary>
        /// <param name="element"></param>
        /// <param name="name"></param>
        /// <returns>the found ConnectorTag or null</returns>
        public static EA.ConnectorTag findTaggedValue(EA.Connector element, String name)
        {
            element.TaggedValues.Refresh();
            foreach (EA.ConnectorTag tag in element.TaggedValues)
            {
                if (tag.Name == name)
                    return tag;
            }

            return null;
        }

        public static EA.TaggedValue findTaggedValue(EA.ConnectorEnd connectorend, String name)
        {
            connectorend.TaggedValues.Refresh();
            foreach (EA.TaggedValue tag in connectorend.TaggedValues)
            {
                if (tag.Name == name)
                    return tag;
            }

            return null;
        }

        public static Object findTaggedValue(Object eaObject, String name)
        {
            if (eaObject is EA.Element)
                return findTaggedValue(eaObject as EA.Element, name);
            else if (eaObject is EA.Connector)
                return findTaggedValue(eaObject as EA.Connector, name);
            else if (eaObject is EA.ConnectorEnd)
                return findTaggedValue(eaObject as EA.ConnectorEnd, name);
            else if (eaObject is EA.Package)
                return findTaggedValue(eaObject as EA.Package, name);
            else if (eaObject is EA.Method)
                return findTaggedValue(eaObject as EA.Method, name);
            return null;
        }

        /// <summary>
        /// Searches for a TaggedValue in the packageElement with the given name
        /// </summary>
        /// <param name="element"></param>
        /// <param name="name"></param>
        /// <returns>the found TaggedValue or null</returns>
        public static EA.TaggedValue findTaggedValue(EA.Package package, String name)
        {
            EA.TaggedValue aTag = null;
            EA.Element element = package.Element;
            if (element != null)
            {
                element.TaggedValues.Refresh();


                foreach (EA.TaggedValue aTaggi in element.TaggedValues)
                {
                    if (aTaggi.Name == name) aTag = aTaggi;
                }
            }

            return aTag;
        }

        public static string getPackageURI(EA.Repository repository, EA.Package pkg)
        {
            // Check for user defined uri
            EA.TaggedValue nsURI = findTaggedValue(pkg, "nsURI");
            if (nsURI != null)
                return nsURI.Value + "#/";

            // Determine URI according to Moflon convention
            String pathToPackage = "";
            EA.Package outermostPackage = null;
            while (pkg.ParentID != 0)
            {
                EA.Package parent = repository.GetPackageByID(pkg.ParentID);

                if (parent.ParentID == 0)
                    outermostPackage = pkg;
                else
                    pathToPackage = "/" + pkg.Name + pathToPackage;

                pkg = repository.GetPackageByID(pkg.ParentID);
            }

            return "http://www.moflon.org/" + outermostPackage.Name + "#/" + pathToPackage;
        }

        public static string getPackageURI(EA.Repository repository, EA.Element targetElement)
        {
            return getPackageURI(repository, repository.GetPackageByID(targetElement.PackageID)) + "/";
        }

        private static int markConnectorOnDiagram(EA.Connector connector, SQLRepository repository)
        {

            int count = 0;
            List<String> diagramIdsOfClient = EAUtil.getDiagramIDsOfObject(repository.GetElementByID(connector.ClientID), repository);
            List<String> diagramIdsOfSupplier = EAUtil.getDiagramIDsOfObject(repository.GetElementByID(connector.SupplierID), repository);
            int diagramId = 0;
            foreach (String clientDiagId in diagramIdsOfClient)
            {
                foreach (String suppDiagId in diagramIdsOfSupplier)
                {
                    if (clientDiagId != "" && suppDiagId != "")
                    {
                        if (clientDiagId == suppDiagId)
                        {
                            diagramId = int.Parse(clientDiagId);
                            count++;
                        }
                    }
                }
            }
            if (diagramId == 0)
                return 0;
            EA.Diagram diagram = repository.GetOriginalRepository().GetDiagramByID(diagramId);

            EA.DiagramLink diagramObject = null;

            foreach (EA.DiagramLink diagLink in diagram.DiagramLinks)
            {
                if (diagLink.ConnectorID == connector.ConnectorID)
                    diagramObject = diagLink;
            }
            if (diagramObject != null)
            {
                repository.OpenDiagram(diagramObject.DiagramID);
                repository.GetCurrentDiagram().SelectedConnector = connector;
                repository.SaveDiagram(diagramObject.DiagramID);
                //repository.ReloadDiagram(diagramObject.DiagramID);
            }
            return count;
        }

        public static EA.TaggedValue setTaggedValue(SQLRepository repository, EA.Package package, String name, String value)
        {
            EA.Package actPackage = package;
            if (actPackage is EAPackage)
                actPackage = repository.GetOriginalRepository().GetPackageByID(package.PackageID);
            EA.Element element = actPackage.Element;
            if (element != null)
            {
                EA.TaggedValue aTag = findTaggedValue(element, name);

                if (aTag == null) aTag = (EA.TaggedValue)element.TaggedValues.AddNew(name, value);
                aTag.Value = value;
                aTag.Update();
                return aTag;
            }
            return null;
        }

        public static EA.TaggedValue setTaggedValueNotes(SQLRepository repository, EA.Element element, String name, String notes)
        {
            element.TaggedValues.Refresh();
            EA.Element actElement = element;
            if (actElement is EAElement)
                actElement = repository.GetOriginalRepository().GetElementByID(element.ElementID);
            EA.TaggedValue aTag = setTaggedValue(repository, actElement, name, "<memo>");
            aTag.Notes = notes;
            aTag.Update();
            return aTag;
        }

        public static EA.TaggedValue setTaggedValueNotes(SQLRepository repository, EA.Package package, String name, String notes)
        {
            if (package.Element != null)
            {
                package.Element.TaggedValues.Refresh();
                EA.TaggedValue aTag = setTaggedValue(repository, package, name, "<memo>");
                aTag.Notes = notes;
                aTag.Update();
                return aTag;
            }
            return null;

        }

        public static Object setTaggedValueNotes(SQLRepository repository, Object eaObject, String name, String notes)
        {
            if (eaObject is EA.Package)
                return setTaggedValueNotes(repository, eaObject as EA.Package, name, notes);
            else if (eaObject is EA.Element)
                return setTaggedValueNotes(repository, eaObject as EA.Element, name, notes);
            else if (eaObject is EA.Connector)
                return setTaggedValueNotes(repository, eaObject as EA.Connector, name, notes);
            else if (eaObject is EA.ConnectorEnd)
                return setTaggedValueNotes(repository, eaObject as EA.ConnectorEnd, name, notes);
            return null;
        }

        public static EA.ConnectorTag setTaggedValueNotes(SQLRepository repository, EA.Connector connector, String name, String notes)
        {
            connector.TaggedValues.Refresh();
            EA.ConnectorTag aTag = setTaggedValue(repository, connector, name, "<memo>");
            aTag.Notes = notes;
            aTag.Update();
            return aTag;
        }

        public static EA.TaggedValue setTaggedValue(SQLRepository repository, EA.Element elem, String name, String value)
        {
            EA.Element element = elem;
            if (elem is EAElement)
                element = repository.GetOriginalRepository().GetElementByID(elem.ElementID);
            element.TaggedValues.Refresh();
            EA.TaggedValue aTag = findTaggedValue(element, name);
            if (aTag == null) aTag = (EA.TaggedValue)element.TaggedValues.AddNew(name, value);
            aTag.Value = value;
            aTag.Update();
            return aTag;
        }
        /// <summary>
        /// Creates or editExistingLink the ConnectorTag with the given name and value of the given connector
        /// </summary>
        /// <param name="element"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns>the created or edited ConnectorTag</returns>
        public static EA.ConnectorTag setTaggedValue(SQLRepository repository, EA.Connector con, String name, String value)
        {
            EA.Connector connector = con;
            if (con is EAConnector)
                connector = repository.GetOriginalRepository().GetConnectorByID(con.ConnectorID);
            connector.TaggedValues.Refresh();
            EA.ConnectorTag aTag = findTaggedValue(connector, name);
            if (aTag == null) aTag = (EA.ConnectorTag)connector.TaggedValues.AddNew(name, value);
            aTag.Value = value;
            aTag.Update();
            return aTag;
        }
        /// <summary>
        /// Creates or editExistingLink the MethodTag with the given name and value of the given method
        /// </summary>
        /// <param name="element"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns>the created or edited MethodTag</returns>
        public static EA.MethodTag setTaggedValue(SQLRepository repository, EA.Method meth, String name, String value)
        {
            EA.Method method = meth;
            if (meth is EAMethod)
                method = repository.GetOriginalRepository().GetMethodByID(meth.MethodID);
            method.TaggedValues.Refresh();
            EA.MethodTag aTag = findTaggedValue(method, name);
            if (aTag == null) aTag = (EA.MethodTag)method.TaggedValues.AddNew(name, value);
            aTag.Value = value;
            aTag.Update();
            return aTag;
        }

        public static bool deleteTaggedValue(EA.Element element, String name)
        {
            element.TaggedValues.Refresh();
            short z = 0;
            foreach (EA.TaggedValue aTag in element.TaggedValues)
            {
                if (aTag.Name == name)
                {
                    element.TaggedValues.Delete(z);
                    return true;
                }
                z++;
            }
            return false;
        }

        public static Object sqlEAObjectToOriginalObject(SQLRepository sqlRepository, Object sqlEAObject)
        {
            EA.Repository repository = sqlRepository.GetOriginalRepository();
            Object realObject = null;
            if (sqlEAObject is EA.Element)
            {
                realObject = repository.GetElementByID((sqlEAObject as EA.Element).ElementID);
            }
            else if (sqlEAObject is EA.Connector)
            {
                realObject = repository.GetConnectorByID((sqlEAObject as EA.Connector).ConnectorID);
            }
            else if (sqlEAObject is EA.Method)
            {
                realObject = repository.GetMethodByID((sqlEAObject as EA.Method).MethodID);
            }
            else if (sqlEAObject is EA.Attribute)
            {
                realObject = repository.GetAttributeByID((sqlEAObject as EA.Attribute).AttributeID);
            }
            else if (sqlEAObject is EA.Package)
            {
                realObject = repository.GetPackageByID((sqlEAObject as EA.Package).PackageID);
            }
            else if (sqlEAObject is EA.Diagram)
            {
                realObject = repository.GetDiagramByID((sqlEAObject as EA.Diagram).DiagramID);
            }
            return realObject;
        }

        public static int markObjectOnDiagram(Object eaObject, SQLRepository rep)
        {
            clearSelectedDiagramObjects(rep);
            int count = 0;
            if (eaObject is EA.Element)
                count = markElementOnDiagram(eaObject as EA.Element, rep);
            else if (eaObject is EA.Connector)
                count = markConnectorOnDiagram(eaObject as EA.Connector, rep);
            else if (eaObject is EA.Method)
                count = markMethodOnDiagram(eaObject as EA.Method, rep);
            else if (eaObject is EA.Attribute)
                count = markAttributeOnDiagram(eaObject as EA.Attribute, rep);
            else if (eaObject is EA.Package)
                count = markPackageOnDiagram(eaObject as EA.Package, rep);


            /*
            EA.Diagram curDiag = rep.GetCurrentDiagram();

            if (curDiag != null && curDiag.SelectedObjects.Count > 0)
            {
                KeyboardSimulator kSim = new KeyboardSimulator();

                if (count == 1)
                {

                    kSim.KeyDown(VirtualKeyCode.LCONTROL);
                    kSim.KeyPress(VirtualKeyCode.VK_U);
                    kSim.KeyUp(VirtualKeyCode.LCONTROL);

                }
                if (count > 1)
                {
                    kSim.KeyDown(VirtualKeyCode.LCONTROL);
                    kSim.KeyPress(VirtualKeyCode.VK_U);
                    kSim.KeyUp(VirtualKeyCode.LCONTROL);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.NEXT);
                    kSim.KeyPress(VirtualKeyCode.RETURN);

                }
            }
             * */
            return count;
        }

        private static int markPackageOnDiagram(EA.Package pkg, SQLRepository rep)
        {
            if (pkg.Element != null)
                return markElementOnDiagram(pkg.Element, rep);
            return 0;
        }

        public static void deleteDiagramObject(EA.Repository repository, EA.Diagram actDiagram, EA.Element actElement)
        {
            short i = 0;
            repository.SaveDiagram(actDiagram.DiagramID);
            foreach (EA.DiagramObject actDiagObject in actDiagram.DiagramObjects)
            {
                if (actDiagObject.ElementID == actElement.ElementID)
                {
                    actDiagram.DiagramObjects.Delete(i);
                    actDiagram.DiagramObjects.Refresh();
                    actDiagram.Update();
                    repository.ReloadDiagram(actDiagram.DiagramID);
                    break;
                }
                i++;
            }
        }

        /// <summary>
        /// Searches for a DiagramObject with the same ID as the element on the given diagram
        /// </summary>
        /// <param name="element"></param>
        /// <param name="diagram"></param>
        /// <returns>The found DiagramObject or null if there exists none</returns>
        public static EA.DiagramObject findDiagramObject(SQLRepository repository, EA.Element element, EA.Diagram diagram)
        {
            EA.Diagram currentDiagram = diagram;
            if (currentDiagram is EADiagram)
                currentDiagram = sqlEAObjectToOriginalObject(repository, currentDiagram) as EA.Diagram;
            EA.DiagramObject val = null;
            foreach (EA.DiagramObject diagObj in currentDiagram.DiagramObjects)
            {
                if (diagObj.ElementID == element.ElementID)
                {
                    val = diagObj;
                    break;
                }
            }
            return val;
        }

        /// <summary>
        /// Searches for a DiagramObject with the same ID as the connector on the given diagram
        /// </summary>
        /// <param name="element"></param>
        /// <param name="diagram"></param>
        /// <returns>The found DiagramObject or null if there exists none</returns>
        public static EA.DiagramLink findDiagramObject(EA.Connector element, EA.Diagram diagram)
        {
            EA.DiagramLink val = null;
            foreach (EA.DiagramLink diagObj in diagram.DiagramLinks)
            {
                if (diagObj.ConnectorID == element.ConnectorID) { val = diagObj; break; }
            }
            return val;
        }


        public static bool markObjectInProjectBrowser(Object eaObject, SQLRepository rep)
        {
            if (eaObject is EA.Element)
                return markElementInProjectBrowser(eaObject as EA.Element, rep);
            else if (eaObject is EA.Connector)
                return markConnectorInProjectBrowser(eaObject as EA.Connector, rep);
            else if (eaObject is EA.Method)
                return markMethodInProjectBrowser(eaObject as EA.Method, rep);
            else if (eaObject is EA.Attribute)
                return markAttributeInProjectBrowser(eaObject as EA.Attribute, rep);
            else if (eaObject is EA.Package)
                return markPackageInProjectBrowser(eaObject as EA.Package, rep);
            else
                return false;
        }

        private static Boolean markMethodInProjectBrowser(EA.Method method, SQLRepository rep)
        {
            EA.Method realMethod = rep.GetOriginalRepository().GetMethodByID(method.MethodID);
            rep.ShowInProjectView(realMethod);
            return true;
        }

        private static Boolean markAttributeInProjectBrowser(EA.Attribute attribute, SQLRepository rep)
        {
            EA.Attribute realAttribute = rep.GetOriginalRepository().GetAttributeByID(attribute.AttributeID);
            rep.ShowInProjectView(realAttribute);
            return true;
        }

        private static Boolean markConnectorInProjectBrowser(EA.Connector con, SQLRepository rep)
        {
            EA.Element client = rep.GetElementByID(con.ClientID);

            return markElementInProjectBrowser(client, rep);
        }


        private static bool markElementInProjectBrowser(EA.Element element, SQLRepository repository)
        {
            EA.Element actElement = null;
            if (element is EAElement)
                actElement = repository.GetOriginalRepository().GetElementByGuid(element.ElementGUID);
            else
                actElement = element;
            repository.ShowInProjectView(actElement);
            return true;
        }

        private static Boolean markPackageInProjectBrowser(EA.Package package, SQLRepository repository)
        {
            EA.Package actPackage = null;
            if (package is EAPackage)
                actPackage = repository.GetOriginalRepository().GetPackageByID(package.PackageID);
            else
                actPackage = package;
            repository.ShowInProjectView(actPackage);
            //repository.GetTreeSelectedElements().AddNew( = actPackage;
            return true;
        }

        private static int markAttributeOnDiagram(EA.Attribute attribute, SQLRepository repository)
        {
            EA.Element parentElement = repository.GetElementByID(attribute.ParentID);
            int count = markElementOnDiagram(parentElement, repository);
            return count;
        }

        private static int markMethodOnDiagram(EA.Method method, SQLRepository repository)
        {
            EA.Element parentElement = repository.GetElementByID(method.ParentID);
            return markElementOnDiagram(parentElement, repository);
        }

        public static void clearSelectedDiagramObjects(SQLRepository rep)
        {
            EA.Diagram diagram = rep.GetCurrentDiagram();
            if (diagram != null)
            {
                for (short i = 0; i < diagram.SelectedObjects.Count; i++)
                {
                    diagram.SelectedObjects.Delete(i);
                }
            }
        }

        private static int markElementOnDiagram(EA.Element element, SQLRepository repository)
        {
            EA.Element realElement = element;
            EA.DiagramObject diagramObject = null;
            String diagramIdResult = repository.SQLQuery("select * from t_diagramobjects where t_diagramobjects.Object_ID = " + realElement.ElementID);
            int count = 0;
            String diagramID = EAEcoreAddin.Util.EAUtil.getXMLNodeContentFromSQLQueryString(diagramIdResult, "Diagram_ID")[0];
            if (diagramID != "")
            {
                count = EAEcoreAddin.Util.EAUtil.getXMLNodeContentFromSQLQueryString(diagramIdResult, "Diagram_ID").Count;

                EA.Diagram diagram = repository.GetOriginalRepository().GetDiagramByID(int.Parse(diagramID));
                foreach (EA.DiagramObject diagObject in diagram.DiagramObjects)
                {
                    if (diagObject.ElementID == realElement.ElementID)
                        diagramObject = diagObject;
                }
                if (diagramObject != null)
                {
                    repository.OpenDiagram(diagram.DiagramID);

                    diagram.SelectedObjects.AddNew(diagramObject.ElementID.ToString(), "");

                    // repository.ReloadDiagram(diagram.DiagramID);
                }
            }
            return count;

        }

        /// <summary>
        /// Deletes the element
        /// </summary>
        /// <param name="Element"></param>
        /// <param name="Repository"></param>
        public static void deleteElement(EA.Element Element, EA.Repository Repository)
        {

            if (Element.ParentID != 0)
            {
                EA.Element actobject = null;
                EA.Element eleParent = Repository.GetElementByID(Element.ParentID);
                for (short i = 0; i < eleParent.Elements.Count; i++)
                {
                    actobject = (EA.Element)eleParent.Elements.GetAt(i);

                    if (actobject.ElementID == Element.ElementID)
                    {
                        eleParent.Elements.Delete(i);
                        eleParent.Elements.Refresh();
                        Element.Update();
                        return;
                    }
                }

            }
            else
            {
                EA.Element actobject = null;
                EA.Package eleParent = Repository.GetPackageByID(Element.PackageID);
                for (short i = 0; i < eleParent.Elements.Count; i++)
                {
                    actobject = (EA.Element)eleParent.Elements.GetAt(i);

                    if (actobject.ElementID == Element.ElementID)
                    {
                        eleParent.Elements.Delete(i);
                        eleParent.Elements.Refresh();
                        Element.Update();
                        return;
                    }
                }
            }



        }

        public static void deletePackage(EA.Package package, EA.Repository Repository)
        {
            if (package.ParentID != 0)
            {
                EA.Package eleParent = Repository.GetPackageByID(package.ParentID);
                EA.Package actobject = null;
                for (short i = 0; i < eleParent.Packages.Count; i++)
                {
                    actobject = (EA.Package)eleParent.Packages.GetAt(i);

                    if (actobject.PackageID == package.PackageID)
                    {
                        eleParent.Packages.Delete(i);
                        eleParent.Packages.Refresh();
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// deletes the connector con
        /// </summary>
        /// <param name="con"></param>
        /// <param name="Repository"></param>
        public static void deleteConnector(EA.Connector con, EA.Repository Repository)
        {
            EA.Element client = Repository.GetElementByID(con.ClientID);
            EA.Diagram actDiag = Repository.GetCurrentDiagram();
            short i = 0;
            foreach (EA.Connector actLink in client.Connectors)
            {
                if (actLink.ConnectorID == con.ConnectorID)
                {
                    client.Connectors.Delete(i);
                }
                i++;
            }
            Repository.ReloadDiagram(actDiag.DiagramID);
        }

        public static string computeRandomString(Int64 Length)
        {
            System.Random rnd = new System.Random();
            StringBuilder Temp = new StringBuilder();
            for (Int64 i = 0; i < Length; i++)
            {
                Temp.Append(Convert.ToChar(((byte)rnd.Next(254))).ToString());
            }
            return Temp.ToString();
        }

    }
}
